package top.codedance.iotp.client.manage.net.listenner;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.client.manage.ManageApplication;
import top.codedance.iotp.client.manage.net.ComServer;
import top.codedance.iotp.client.manage.net.com232.CmdRunner;
import top.codedance.iotp.client.manage.net.com232.DownloadRunner;
import top.codedance.iotp.client.manage.net.com232.UploadRunner;
import top.codedance.iotp.common.util.BytesBuilder;
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import org.apache.log4j.Logger;

import java.nio.charset.StandardCharsets;

public class SerialPortListener implements SerialPortDataListener {
    private Logger logger = ManageApplication.getLogger(SerialPortListener.class);
    private static final short DOWNLOAD_FILE_RUNNER = 3;
    private static final short UPLOAD_FILE_RUNNER = 2;
    private static final short PLUGING_RUNNER = 1;
    private static final short COMMAND_RUNNER = 0;

    private ComServer cs;
    private BytesBuilder gbb = new BytesBuilder();
    private long lastUpdateTime = 0;

    public SerialPortListener(ComServer cs) {
        this.cs = cs;

        clean();
    }

    @Override
    public int getListeningEvents() {
        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
    }

    public synchronized void serialEvent(SerialPortEvent serialPortEvent) {
        BytesBuilder bb = new BytesBuilder();
        SerialPort serialPort = serialPortEvent.getSerialPort();
        int avai = serialPort.bytesAvailable();
        while (avai > 0) {
            byte[] data = new byte[avai];
            serialPort.readBytes(data, avai);
            bb.append(data);
            avai = serialPort.bytesAvailable();
        }
        try {
            synchronized (this) {
                gbb.append(bb.toArray());
                JSONObject command = JSON.parseObject(new String(gbb.toArray(), StandardCharsets.UTF_8));
                try {
                    JSONObject cmd = command.getJSONObject("cmd");
                    if (cmd.containsKey("runner")){
                        short runnner = cmd.getShort("runner").shortValue();
                        cmd.remove("runner");
                        switch (runnner){
                            case DOWNLOAD_FILE_RUNNER:
                                downloadRunner(cmd, serialPort);
                                break;
                            case UPLOAD_FILE_RUNNER:
                                uploadRunner(cmd, serialPort);
                                break;
                            case PLUGING_RUNNER:
                                pluginRunner(cmd, serialPort);
                                break;
                            case COMMAND_RUNNER:
                            default:
                                throw new Exception("To run cmd");
                        }
                    }else{
                        throw new Exception("To run cmd");
                    }
                } catch (Exception e) {
                    cmdRunner(command, serialPort);
                }
            }
        } catch (Exception ex) {

        } finally {
            lastUpdateTime = System.currentTimeMillis();
        }
    }

    private void clean() {
        new Thread(() -> {
            while (true) {
                try {
                    if (System.currentTimeMillis() - lastUpdateTime > 1000 * 5) {
                        synchronized (this) {
                            if (gbb.length() > 0) {
                                logger.debug("Discard data: " + gbb.length() + " byte.");
                                gbb = new BytesBuilder();
                                lastUpdateTime = System.currentTimeMillis();
                            }
                        }
                    }
                    Thread.sleep(100);
                } catch (Exception e) {
                    logger.debug(e.getMessage());
                }
            }
        }).start();
    }

    /**
     * 系统命令执行器
     * @param command
     * @param serialPort
     */
    private void cmdRunner(JSONObject command, SerialPort serialPort) {
        CmdRunner runner = new CmdRunner();
        logger.debug(command.toJSONString());
        byte[] result = runner.exec(command.toJSONString().getBytes(StandardCharsets.UTF_8));
        logger.debug(new String(result, StandardCharsets.UTF_8));
        gbb = new BytesBuilder();
        serialPort.writeBytes(result, result.length);
    }

    /**
     * 插件执行器
     * @param cmd
     * @param serialPort
     */
    private void pluginRunner(JSONObject cmd, SerialPort serialPort) {
        logger.debug("manage: " + cmd.toJSONString());
        byte[] result = ManageApplication.clientRunner.exec((cmd.toJSONString() + "\n").getBytes(StandardCharsets.UTF_8), serialPort);
        logger.debug(new String(result, StandardCharsets.UTF_8));
        gbb = new BytesBuilder();
    }

    /**
     * 上传执行器
     * @param cmd
     * @param serialPort
     */
    private void uploadRunner(JSONObject cmd, SerialPort serialPort){
        UploadRunner runner = new UploadRunner();
        byte[] result = runner.exec(cmd.toJSONString().getBytes(StandardCharsets.UTF_8));
        logger.debug(new String(result, StandardCharsets.UTF_8));
        gbb = new BytesBuilder();
        serialPort.writeBytes(result, result.length);
    }

    /**
     * 下载执行器
     * @param cmd
     * @param serialPort
     */
    private void downloadRunner(JSONObject cmd, SerialPort serialPort){
        DownloadRunner runner = new DownloadRunner();
        runner.exec(cmd.toJSONString().getBytes(StandardCharsets.UTF_8), serialPort);
        gbb = new BytesBuilder();
    }
}
